package com.web.exiaodao.remote.controller;

import com.unionpay.acp.sdk.wap.*;
import com.web.exiaodao.business.constant.JConstant;
import com.web.exiaodao.business.entity.Order;
import com.web.exiaodao.remote.service.ApiOrderService;
import com.web.webstart.base.controller.BaseController;
import com.web.webstart.base.util.DateUtils;
import com.web.webstart.base.util.XaUtil;
import com.web.webstart.base.util.result.XaResult;
import com.wordnik.swagger.annotations.Api;
import com.wordnik.swagger.annotations.ApiOperation;
import com.wordnik.swagger.annotations.ApiParam;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;

import static com.web.exiaodao.business.constant.JConstant.SERVER_ADDRESS;
import static com.web.exiaodao.business.constant.JConstant.SERVER_NAME;

/**
 * @author zhanglin
 * @version V1.0
 * @Title: ApiPayUnionpayController.java
 * @Package com.web.liuda.remote.controller
 * @Description: 可以参见 手机控件支付开发包（安卓版,里面有app和后台的代码;
 * https://open.unionpay.com/ajweb/help/file/techFile?productId=3
 * @date 2016/12/9 上午10:31
 */
@Api(value = "pay_unionpay", description = "银联支付接口", position = 10)
@Controller
@RequestMapping("/api/unionpay")
public class ApiPayUnionpayController extends BaseController {

    private static final Logger LOGGER = Logger.getLogger(ApiOrderController.class);

    @Autowired
    ApiOrderService orderService;

    /**
     * @return
     * @Title: getTns 获取银联支付的tn吗,tn码是下单之后,根据订单号在次生成的
     * @Description: 获取tn码
     */
    @ApiOperation(value = "银联获取tn码", notes = "银联获取tn码")
    @ResponseBody
    @RequestMapping(value = "getTns", method = RequestMethod.POST)
    public XaResult<String> getTns(
            @ApiParam("订单编号,字段名:orderNo") @RequestParam(value = "OrderNo") String orderNo,
            HttpServletRequest request,
            HttpServletResponse reponse
    ) throws Exception {
        XaResult<String> xr = new XaResult<String>();
        if (XaUtil.isEmpty(orderNo)) {
            xr.error("OrderNo不能为空");
            return xr;
        }

        //判断订单是否存在
        String txnAmt = "";//交易金额 单位为分，不能带小数点
        Order order = orderService.getOrder(orderNo);
        if (XaUtil.isEmpty(order)) {
            xr.error("该订单不存在");
            return xr;
        }
        if (Integer.parseInt(order.getOrderState() + "") != JConstant.OrderStatus.UN_PAY) {
            xr.error("该订单无效(只有未支付状态才能获取tn)！");
            return xr;
        }
        txnAmt = new java.text.DecimalFormat("0").format(order.getOrderTotalPirce() * 100);
        if (txnAmt.contains(JConstant.SYMBOL_DOT)) {
            xr.error("价格错误,银联支付不能包含小数点");
            return xr;
        }

        //设置参数
        String merId = BaseConfig.merId;
        String txnTime = DateUtils.getTodayStrSimple();

        Map<String, String> contentData = new HashMap<String, String>();
        /***银联全渠道系统，产品参数，除了encoding自行选择外其他不需修改***/
        contentData.put("version", SDKUtil.version);            //版本号 全渠道默认值
        contentData.put("encoding", SDKUtil.encoding_UTF8);     //字符集编码 可以使用UTF-8,GBK两种方式
        contentData.put("signMethod", "01");                    //签名方法 目前只支持01：RSA方式证书加密
        contentData.put("txnType", "01");                        //交易类型 01:消费
        contentData.put("txnSubType", "01");                    //交易子类 01：消费
        contentData.put("bizType", "000201");                    //填写000201
        contentData.put("channelType", "07");                    //渠道类型
        /***商户接入参数***/
        contentData.put("merId", merId);                        //商户号码，请改成自己申请的商户号或者open上注册得来的777商户号测试
        contentData.put("accessType", "0");                        //接入类型，商户接入填0 ，不需修改（0：直连商户， 1： 收单机构 2：平台商户）
        contentData.put("orderId", orderNo);                    //商户订单号，8-40位数字字母，不能含“-”或“_”，可以自行定制规则
        contentData.put("txnTime", txnTime);                    //订单发送时间，取系统时间，格式为YYYYMMDDhhmmss，必须取当前时间，否则会报txnTime无效
        contentData.put("accType", "01");                        //账号类型 01：银行卡02：存折03：IC卡帐号类型(卡介质)
        contentData.put("txnAmt", txnAmt);                        //交易金额 单位为分，不能带小数点
        contentData.put("currencyCode", "156");                 //境内商户固定 156 人民币
        contentData.put("reqReserved", "透传字段");                //商户自定义保留域，交易应答时会原样返回
        //后台通知地址
        String backUrl = "";
        backUrl = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + "/" + SERVER_NAME + "/api/unionpay/backUrl";
        backUrl = JConstant.ENV == 1 ? backUrl : SERVER_ADDRESS + SERVER_NAME + "/api/unionpay/backUrl";

        contentData.put("backUrl", backUrl);
        //从classpath加载acp_sdk.properties文件
        String base_url = request.getSession().getServletContext().getRealPath("/");
        SDKConfig.getConfig().loadPropertiesFromPath(null, base_url);

        /**对请求参数进行签名并发送http post请求，接收同步应答报文**/
        System.out.println("sign start.........");
        Map<String, String> submitFromData = SDKUtil.signData(contentData, SDKUtil.encoding_UTF8);
        System.out.println("sign end.........");//报文中certId,signature的值是在signData方法中获取并自动赋值的，只要证书配置正确即可。
        String requestAppUrl = SDKConfig.getConfig().getAppRequestUrl();                                 //交易请求url从配置文件读取对应属性文件acp_sdk.properties中的 acpsdk.backTransUrl
        //如果这里通讯读超时（30秒），需发起交易状态查询交易
        Map<String, String> resmap = SDKUtil.submitUrl(submitFromData, requestAppUrl, SDKUtil.encoding_UTF8);  //发送请求报文并接受同步应答（默认连接超时时间30秒，读取返回结果超时时间30秒）;这里调用signData之后，调用submitUrl之前不能对submitFromData中的键值对做任何修改，如果修改会导致验签不通过
        /**对应答码的处理，请根据您的业务逻辑来编写程序,以下应答码处理逻辑仅供参考------------->**/
        //应答码规范参考open.unionpay.com帮助中心 下载  产品接口规范  《平台接入接口规范-第5部分-附录》
        String respCode = resmap.get("respCode");
        if (("00").equals(respCode)) {
            //成功,获取tn号
            String tn = resmap.get("tn");
            xr.setObject(tn);
        } else {
            //其他应答码为失败请排查原因
            xr.error("获取tn码失败");
            return xr;
        }
        return xr;
}

    /**
     * @return
     * @Title: backUrl
     * @Description: 银联回调地址
     */
    @ApiOperation(value = "银联回调地址", notes = "银联回调地址")
    @ResponseBody
    @RequestMapping(value = "backUrl", method = RequestMethod.POST)
    public void backUrl(
            HttpServletRequest request,
            HttpServletResponse reponse
    ) throws Exception {
        reponse.reset();
        PrintWriter out = reponse.getWriter();
        LogUtil.writeLog("BackRcvResponse接收后台通知开始");
        request.setCharacterEncoding("ISO-8859-1");
        String encoding = request.getParameter(SDKConstants.param_encoding);
        // 获取银联通知服务器发送的后台通知参数
        Map<String, String> reqParam = getAllRequestParam(request);
        LogUtil.printRequestLog(reqParam);
        Map<String, String> valideData = null;
        if (null != reqParam && !reqParam.isEmpty()) {
            Iterator<Entry<String, String>> it = reqParam.entrySet().iterator();
            valideData = new HashMap<String, String>(reqParam.size());
            while (it.hasNext()) {
                Entry<String, String> e = it.next();
                String key = (String) e.getKey();
                String value = (String) e.getValue();
                value = new String(value.getBytes("ISO-8859-1"), encoding);
                valideData.put(key, value);
            }
        }
        //重要！验证签名前不要修改reqParam中的键值对的内容，否则会验签不过
        if (!SDKUtil.validate(valideData, encoding)) {
            LogUtil.writeLog("验证签名结果[失败].");
            //验签失败，需解决验签问题
        } else {
            LogUtil.writeLog("验证签名结果[成功].");
            //【注：为了安全验签成功才应该写商户的成功处理逻辑】交易成功，更新商户订单状态
            String orderId = valideData.get("orderId"); //获取后台通知的数据，其他字段也可用类似方式获取
            String queryId = valideData.get("queryId"); //获取后台通知的数据，其他字段也可用类似方式获取
            orderService.modifyOrder(orderId, null, queryId, JConstant.PayType.UNIONPAY);
        }
        LogUtil.writeLog("BackRcvResponse接收后台通知结束");
        //返回给银联服务器http 200  状态码
        out.print("ok");
    }

    /**
     * @return
     * @Title: backUrlRefund
     * @Description: 银联退款回调地址
     */
    @ApiOperation(value = "银联退款回调地址", notes = "银联退款回调地址")
    @ResponseBody
    @RequestMapping(value = "backUrlRefund", method = RequestMethod.POST)
    public void backUrlRefund(
            HttpServletRequest request,
            HttpServletResponse reponse
    ) throws Exception {
        reponse.reset();
        PrintWriter out = reponse.getWriter();
        LogUtil.writeLog("BackRcvResponse接收后台通知开始");
        request.setCharacterEncoding("ISO-8859-1");
        String encoding = request.getParameter(SDKConstants.param_encoding);
        // 获取银联通知服务器发送的后台通知参数
        Map<String, String> reqParam = getAllRequestParam(request);
        LogUtil.printRequestLog(reqParam);
        Map<String, String> valideData = null;
        if (null != reqParam && !reqParam.isEmpty()) {
            Iterator<Entry<String, String>> it = reqParam.entrySet().iterator();
            valideData = new HashMap<String, String>(reqParam.size());
            while (it.hasNext()) {
                Entry<String, String> e = it.next();
                String key = (String) e.getKey();
                String value = (String) e.getValue();
                value = new String(value.getBytes("ISO-8859-1"), encoding);
                valideData.put(key, value);
            }
        }
        //重要！验证签名前不要修改reqParam中的键值对的内容，否则会验签不过
        if (!SDKUtil.validate(valideData, encoding)) {
            LogUtil.writeLog("验证签名结果[失败].");
            //验签失败，需解决验签问题
            String orderId = valideData.get("orderId"); //获取后台通知的数据，其他字段也可用类似方式获取
            orderService.modifyOrderRefund(orderId, JConstant.OrderRefundStatus.REFUND_FAIL, JConstant.PayType.UNIONPAY);
        } else {
            LogUtil.writeLog("验证签名结果[成功].");
            //【注：为了安全验签成功才应该写商户的成功处理逻辑】交易成功，更新商户订单状态
            String orderId = valideData.get("orderId"); //获取后台通知的数据，其他字段也可用类似方式获取
            orderService.modifyOrderRefund(orderId, JConstant.OrderRefundStatus.REFUND_SUCCESS, JConstant.PayType.UNIONPAY);
        }
        LogUtil.writeLog("BackRcvResponse接收后台通知结束");
        //返回给银联服务器http 200  状态码
        out.print("ok");
    }

    /**
     * 获取请求参数中所有的信息
     *
     * @param request
     * @return
     */
    public static Map<String, String> getAllRequestParam(final HttpServletRequest request) {
        Map<String, String> res = new HashMap<String, String>();
        Enumeration<?> temp = request.getParameterNames();
        if (null != temp) {
            while (temp.hasMoreElements()) {
                String en = (String) temp.nextElement();
                String value = request.getParameter(en);
                res.put(en, value);
                //在报文上送时，如果字段的值为空，则不上送<下面的处理为在获取所有参数数据时，判断若值为空，则删除这个字段>
                if (null == res.get(en) || "".equals(res.get(en))) {
                    res.remove(en);
                }
            }
        }
        return res;
    }


}
